'use strict'

const http = require('http')
const fs = require('fs')

const fsp = fs.promises

function parseBodyHeaders(data) {
  let headers = {}
  let dsplit = data.split('\r\n').filter(p => p.length > 0)

  let ind
  let k
  for (let d of dsplit) {
    ind = d.indexOf(':')

    if (ind <= 0) continue
    
    k = d.substring(0, ind).toLowerCase()

    headers[k] = d.substring(ind+1).trim()

  }

  let cpd = headers['content-disposition']

  //按照; 分割成数组，第一个元素form-data去掉
  let clist = cpd.split('; ').slice(1)

  let name, filename
  
  for (let a of clist) {
    ind = a.indexOf('filename="')
    if (ind >= 0) {
      filename = a.substring(ind+10, a.length-1)
    } else {
      name = a.substring(6, a.length - 1)
    }
  }

  return {
    headers,
    name,
    filename
  }

}

/**
 * 
 * @param {Buffer} bodyData 原始的Body数据
 * @param {Object} reqHeaders reuqest.headers
 */

function parseBody(bodyData, reqHeaders) {
  
  //content-type: multipart/form-data; boundary=-----34294329
  let ctype = reqHeaders['content-type']

  //拿到分界线boundary
  let bdy = ctype.substring( ctype.indexOf('=')+1 )

  //数据中用于分割文件的分界线
  //let bdy = `--${bdy}\r\n`

  let crlf_bdy = `\r\n--${bdy}`

  //从偏移bdy的长度开始查找下一个分界线的位置
  let data_end_index = bodyData.indexOf(crlf_bdy, crlf_bdy.length)

  //body消息数据中header部分的最后索引
  let header_end_index = bodyData.indexOf('\r\n\r\n', crlf_bdy.length)

  let header_data = bodyData.toString('utf8', 
                                  crlf_bdy.length, 
                                  header_end_index);

  //解析body数据中的消息头
  let hd = parseBodyHeaders(header_data)
  
  let fileinfo = {
    start: header_end_index+4,
    end: data_end_index,
  }

  fileinfo.length = fileinfo.end - fileinfo.start

  return {
    name: hd.name,
    filename: hd.filename,
    headers: hd.headers,
    start: fileinfo.start,
    end: fileinfo.end,
    length: fileinfo.length
  }

}

function parseExtName(filename){
  if(filename.length < 2) return ''

  let namesplit = filename.split('.').filter(p => p.length >0)

  if(namesplit.length < 2) return ''

  return `.${namesplit[namesplit.length - 1]}`


}

let routerTable = {

  GET: {
    '/upload' : async (ctx) => {
        let stm = fs.createReadStream('./upload.html')
        stm.pipe(ctx.res)
        stm.on('end', () => {
          ctx.end()
        })
    }
  },

  POST: {
    '/upload' : async (ctx) => {
        console.log(ctx.files)

        let finfo = ctx.files

        let filename = `${Date.now()}-`
                        + `${parseInt(Math.round() * 100000) + 100000}`
                        +parseExtName(finfo.filename)

        let fh = await fsp.open(filename, 'w+')


        let fret = await fh.write(ctx.rawBody,
                            finfo.start,
                            finfo.length,
                          0)
        
        //关闭打开的文件
        fh.close()

        //返回写入的字节数，string或buffer类型，不能是其他类型
        ctx.end( `${fret.bytesWritten}`)
    }
  }

}

/**==========================START SERVER========================== */

let serv = http.createServer()

serv.on('request', (request, response) => {
  let rm = routerTable[request.method]

  //根据？切分字符串
  let usplit = request.url.split('?');

  let pathname = usplit[0]
  let querystring = usplit[1] || ''

  if (!rm || !rm[ pathname ]) {
    response.statusCode = 404
    response.end('page not found')
    return
  }

  //ctx is content，创造请求上下文对象
  let ctx = {
    req: request,
    res: response,
    method: request.method,
    headers: request.headers,
    end: (data, encoding = "utf-8") => {
      response.end(data, encoding)
    },
    write: (data, encoding = "utf-8") => {
      response.write(data, encoding)
    },
    path: pathname,
    querystring: querystring,
    rawBody: null
  }

  let bufferList = []
  let totalLength = 0
 
  if('PD'.indexOf(request.method[0]) >= 0) {
    request.on('data', chunk => {
      totalLength += chunk.length
      bufferList.push(chunk)
    })
  } else {
    request.on('data', chunk => {
      response.statusCode = 400
      response.end()
    })
  }

  request.on('end', () => {
    if(bufferList.length > 0){
      ctx.rawBody = Buffer.concat(bufferList, totalLength)
      bufferList = []
    }
    let ctype = ctx.headers['content-type'] || ""

    if(ctx.rawBody && ctype.indexOf('multipart/form-data') === 0){
      ctx.files = parseBody(ctx.rawBody, ctx.headers)
    }

    rm[ pathname ](ctx)
  })

})

serv.listen(3456)
